/*
 * Project: Sudoku Hint Engine for Android
 * Copyright (C) 2010-2013 Emanuel Moecklin
 * 
 * Available under the terms of the Lesser General Public License (LGPL)
 */
package com.onegravity.sudoku.solver.rules.als;

import java.util.BitSet;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;

import android.net.Uri;

import com.onegravity.sudoku.solver.HintResource;
import com.onegravity.sudoku.solver.HintType;
import com.onegravity.sudoku.solver.core.IndirectHint;
import com.onegravity.sudoku.solver.core.Rule;
import com.onegravity.sudoku.solver.data.Cell;
import com.onegravity.sudoku.solver.data.ImmutableCell;
import com.onegravity.sudoku.solver.data.Region;
import com.onegravity.sudoku.solver.util.BitSetFactory;
import com.onegravity.sudoku.solver.util.HtmlLoader;

/**
 * ALS-XY-Wing
 * @author Emanuel Moecklin
 */
public class ALSXYWingHint extends IndirectHint implements Rule {

	private static final long serialVersionUID = -3087062061987696929L;
	
	private final Set<Cell> alsACells;
    private final Set<Cell> alsBCells;
    private final Set<Cell> alsCCells;
    private final Region alsARegion;
    private final Region alsBRegion;
    private final Region alsCRegion;
    private final Region[] allRegions;

    private final int x;
    private final int y;
    private final int z;

    private Set<Cell> allCells = new TreeSet<Cell>();
    private Map<Cell, BitSet> allPotentials = new HashMap<Cell, BitSet>();
    private Map<Cell, BitSet> potentialsNoXYZ = new HashMap<Cell, BitSet>();
    private Map<Cell, BitSet> potentialsXY = new HashMap<Cell, BitSet>();
    private Map<Cell, BitSet> potentialsZ = new HashMap<Cell, BitSet>();

    public ALSXYWingHint(ALS alsA, ALS alsB, ALS alsC, int x, int y, int z, Map<Cell, BitSet> removablePotentials) {
        super(removablePotentials);
        this.alsACells = alsA.getCells();
        this.alsBCells = alsB.getCells();
        this.alsCCells = alsC.getCells();
        this.alsARegion = alsA.getRegion();
        this.alsBRegion = alsB.getRegion();
        this.alsCRegion = alsC.getRegion();
        this.x = x;
        this.y = y;
        this.z = z;

        // create all regions array, the TreeSet is used to eliminate duplicate regions 
    	Set<Region> regions = new TreeSet<Region>();
    	regions.add(alsARegion);
    	regions.add(alsBRegion);
    	regions.add(alsCRegion);
    	allRegions = regions.toArray(new Region[regions.size()]);

    	allCells.addAll(alsA.getCells());
        allCells.addAll(alsB.getCells());
        allCells.addAll(alsC.getCells());
        BitSet zSet = BitSetFactory.createOneElement(z);
        BitSet xySet = new BitSet();
        xySet.set(x);
        xySet.set(y);
        for (Cell cell:allCells) {
        	// all potentials
        	BitSet potentials = cell.getPotentialValues();
        	allPotentials.put(cell, potentials);
        	// all potentials minux x+y+z
    		BitSet noXYZ = (BitSet) potentials.clone();
    		noXYZ.clear(x);
    		noXYZ.clear(y);
    		noXYZ.clear(z);
        	if (!noXYZ.isEmpty()) potentialsNoXYZ.put(cell, noXYZ);
        	// xy potentials
    		BitSet xandory = (BitSet) potentials.clone();
    		xandory.and(xySet);
        	if (!xandory.isEmpty()) potentialsXY.put(cell, xandory);
        	// z potentials
        	if (potentials.get(z)) {
        		potentialsZ.put(cell, zSet);
        	}
        }
    }

    @Override
    public Region[] getRegions(HintType hintType) {
        return allRegions;
    }

    @Override
    public Collection<Cell> getSelectedCells(HintType hintType) {
    	return (hintType==HintType.BIG_CLUE) ? allCells : null;
    }

    // RCCs
    @Override
    public Map<Cell, BitSet> getAddedPotentials(HintType hintType, int viewNum) {
    	return (hintType==HintType.HINT) ? potentialsXY : null;
    }
    
    // CC
    @Override
    public Map<Cell, BitSet> getSecondarySelectedPotentials(HintType hintType, int viewNum) {
    	return (hintType==HintType.HINT) ? potentialsZ : null;
    }
    
    // ALS potentials
    @Override
    public Map<Cell, BitSet> getSelectedPotentials(HintType hintType, int viewNum) {
    	return (hintType==HintType.HINT) ? potentialsNoXYZ : null;
    }

    @Override
    public Map<ImmutableCell, BitSet> getRemovedPotentials(HintType hintType) {
        return (hintType==HintType.HINT) ? super.getRemovedPotentials(hintType) : null;
    }

    @Override
    public double getDifficulty() {
    	return 8.0;
    }

    @Override
    public String getName() {
    	return HintResource.ALMOST_LOCKED_SET_XY_WING.getName();
    }

    @Override
    public Uri getLink() {
    	return HintResource.ALMOST_LOCKED_SET_XY_WING.getUrl();
	}

    @Override
    public String getHintHtml(HintType hintType) {
    	HintResource hintRes = HintResource.ALMOST_LOCKED_SET_XY_WING;
        String result = HtmlLoader.loadHtml(hintRes.hint(hintType));
        return HtmlLoader.format(result, Cell.toString(alsACells), Cell.toString(alsBCells), Cell.toString(alsCCells),
        						 alsARegion.toFullString(), alsBRegion.toFullString(), alsCRegion.toFullString(), x, y, z);
    }

    @Override
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append(getName());
        builder.append(": "); //$NON-NLS-1$
        builder.append(alsARegion.toFullString() + "/n"); //$NON-NLS-1$
        builder.append(alsACells.toString() + "/n"); //$NON-NLS-1$
        builder.append(alsBRegion.toFullString() + "/n"); //$NON-NLS-1$
        builder.append(alsBCells.toString() + "/n"); //$NON-NLS-1$
        builder.append(alsCRegion.toFullString() + "/n"); //$NON-NLS-1$
        builder.append(alsCCells.toString() + "/n"); //$NON-NLS-1$
        builder.append(" on values "); //$NON-NLS-1$
        builder.append(x + "/" + z); //$NON-NLS-1$
        return builder.toString();
    }

    @Override
    public int getNrOfSteps() {
        return 3;
    }

}